Introduction
Install packages, load libraries
We are going to be using a bunch of packages today.
To install all those except tidyverse which you probably already have.
install.packages(c("gghighlight",
"gganimate",
"patchwork",
"ggrepel",
"gapminder"))
library(tidyverse)
## ── Attaching packages ─────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.3 ✓ purrr 0.3.4
## ✓ tibble 3.0.6 ✓ dplyr 1.0.4
## ✓ tidyr 1.1.2 ✓ stringr 1.4.0
## ✓ readr 1.4.0 ✓ forcats 0.5.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag() masks stats::lag()
library(gghighlight) # for bringing attention to certain parts of your plot
library(gganimate) # for animating
library(patchwork) # for making multi-panel plots
library(ggrepel) # for getting labels to not be on top of your points
# data for today
library(gapminder)
Investigate data
# look at structure
glimpse(gapminder)
## Rows: 1,704
## Columns: 6
## $ country <fct> Afghanistan, Afghanistan, Afghanistan, Afghanistan, Afghani…
## $ continent <fct> Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia, Asia,…
## $ year <int> 1952, 1957, 1962, 1967, 1972, 1977, 1982, 1987, 1992, 1997,…
## $ lifeExp <dbl> 28.801, 30.332, 31.997, 34.020, 36.088, 38.438, 39.854, 40.…
## $ pop <int> 8425333, 9240934, 10267083, 11537966, 13079460, 14880372, 1…
## $ gdpPercap <dbl> 779.4453, 820.8530, 853.1007, 836.1971, 739.9811, 786.1134,…
head(gapminder)
## # A tibble: 6 x 6
## country continent year lifeExp pop gdpPercap
## <fct> <fct> <int> <dbl> <int> <dbl>
## 1 Afghanistan Asia 1952 28.8 8425333 779.
## 2 Afghanistan Asia 1957 30.3 9240934 821.
## 3 Afghanistan Asia 1962 32.0 10267083 853.
## 4 Afghanistan Asia 1967 34.0 11537966 836.
## 5 Afghanistan Asia 1972 36.1 13079460 740.
## 6 Afghanistan Asia 1977 38.4 14880372 786.
# what continents do we have?
unique(gapminder$continent)
## [1] Asia Europe Africa Americas Oceania
## Levels: Africa Americas Asia Europe Oceania
Note, our data is already in tidy-style format.
We will look here just at data from the Americas (North and South America)
# make a df with data only from the Americas
gapminder_americas <- gapminder %>%
filter(continent == "Americas")
# what countries do we have?
unique(gapminder_americas$country)
## [1] Argentina Bolivia Brazil
## [4] Canada Chile Colombia
## [7] Costa Rica Cuba Dominican Republic
## [10] Ecuador El Salvador Guatemala
## [13] Haiti Honduras Jamaica
## [16] Mexico Nicaragua Panama
## [19] Paraguay Peru Puerto Rico
## [22] Trinidad and Tobago United States Uruguay
## [25] Venezuela
## 142 Levels: Afghanistan Albania Algeria Angola Argentina Australia ... Zimbabwe
Plot life expectancy over time, for each country
gapminder_americas %>%
ggplot(aes(x = year, y = lifeExp, group = country, color = country)) +
geom_line()

Too crowded to interpret easily.
What if we want to highlight one particular country of interest? Let’s try the United States.
While we are at it, I will add x and y axis labels, a title, subtitle, and caption with labs().
gapminder_americas %>%
ggplot(aes(x = year, y = lifeExp, group = country, color = country)) +
geom_line() +
gghighlight(country == "United States") +
labs(x = "Year",
y = "Life Expectancy (years)",
title = "Life Expectancy in Countries in the Americas",
subtitle = "From 1952 to 2007",
caption = "Data from gapminder.org")
## Warning: Tried to calculate with group_by(), but the calculation failed.
## Falling back to ungrouped filter operation...
## label_key: country

Faceting
What if we want to see all the data at once, but just be able to better attribute each line to the correct country? We can use the [principle of small multiples](https://en.wikipedia.org/wiki/Small_multiple#:~:text=A%20small%20multiple%20(sometimes%20called,was%20popularized%20by%20Edward%20Tufte.), popularized by Edward Tufte, to make a series of charts all on the same scale to allow comparison between them easily.
We can facet using facet_wrap to create small plots for each country. If you want a certain number of rows or columns you can indicate them by including ncol and nrow in the facet_wrap() statement.
gapminder_americas %>%
ggplot(aes(x = year, y = lifeExp, color = country)) +
geom_line() +
facet_wrap(vars(country)) + # facet_wrap(~country) also works
labs(x = "Year",
y = "Life Expectancy (years)",
title = "Life Expectancy in Countries in the Americas",
subtitle = "From 1952 to 2007",
caption = "Data from gapminder.org")

Now our legend is not necessary, so let’s remove it. Let’s also remove the gray background since its not really doing much for us. We will also change to theme_minimal() to get rid of the grey background which I don’t think we need.
gapminder_americas %>%
ggplot(aes(x = year, y = lifeExp)) +
geom_line(aes(color = country)) +
theme_minimal() +
theme(legend.position = "none") +
facet_wrap(~country) +
labs(x = "Year",
y = "Life Expectancy (years)",
title = "Life Expectancy in Countries in the Americas",
subtitle = "From 1952 to 2007",
caption = "Data from gapminder.org")

Wow better! But now its a bit hard to contextualize the line for each country to the whole dataset.
gghighlight
Let’s bring the rest of data back in, and highlight in each facet the country of interest. We can do this by just adding gghighlight() to our ggplot2 call.
Note: if you want to assign something in R to an object, and then view it, you can put the whole thing in parentheses, without having to call that object back at the end.
(americas_lifeexp <- gapminder_americas %>%
ggplot(aes(x = year, y = lifeExp)) +
geom_line(aes(color = country)) +
gghighlight() +
theme_minimal() +
theme(legend.position = "none") +
facet_wrap(~country) +
labs(x = "Year",
y = "Life Expectancy (years)",
title = "Life Expectancy in Countries in the Americas",
subtitle = "From 1952 to 2007",
caption = "Data from gapminder.org"))
## label_key: country
## Too many data series, skip labeling

Adjusting scales
The default in faceting is that the x and y-axes for each plot are all the same. This aids in the interpretation of each small plot in relation to the others, but sometimes you may want freedom to adjust your axes.
For example, if we wanted to plot population over time, if we used the same scale, it would be really hard to see trends within a country.
(americas_pop <- gapminder_americas %>%
ggplot(aes(x = year, y = pop)) +
geom_line(aes(color = country)) +
theme_minimal() +
theme(legend.position = "none") +
facet_wrap(~country) +
labs(x = "Year",
y = "Population",
title = "Population in Countries in the Americas",
subtitle = "From 1952 to 2007",
caption = "Data from gapminder.org"))

Let’s change the scales so that the y-axis is “free” - i.e., each plot will have an independent y-axis. Note, when you do this, you aren’t really using the principle of small multiples anymore, since the data isn’t all on comparable scales.
gapminder_americas %>%
ggplot(aes(x = year, y = pop)) +
geom_line(aes(color = country)) +
theme_minimal() +
theme(legend.position = "none") +
facet_wrap(~country,
scales = "free_y") +
labs(x = "Year",
y = "Population",
title = "Population of Countries in the Americas",
subtitle = "From 1952 to 2007",
caption = "Data from gapminder.org")

The default for scales is "fixed", but you can also set to be "free_x", "free_y", or "free", which means both x and y are free.
Multi-panel plots
What if I take plots I’ve already made and assemble them together? You can do that simply with the package patchwork().
You can use the syntax: * plot1 + plot2 to get two plots next to each other * plot1 / plot2 to get two plots stacked vertically * plot1 | (plot2 + plot3) to get plot1 in the first row, and plots 2 and 3 in a second row
You can use plot_annotation() to indicate your plots with letters or numbers.
I am going to make some quick plots so we can see how it works. Let’s look at some plots of the United States.
# make df with just United States data
gapminder_usa <- gapminder %>%
filter(country == "United States")
# make some plots
(usa_lifeexp <- gapminder_usa %>%
ggplot(aes(x = year, y = lifeExp)) +
geom_point())

(usa_gdppercap <- gapminder_usa %>%
ggplot(aes(x = year, y = gdpPercap)) +
geom_line())

(usa_pop <- gapminder_usa %>%
ggplot(aes(x = year, y = pop)) +
geom_col())

Make multi-panel plots. If you need to wrap around a line, make sure you don’t start your line with the +, it won’t work.
(usa_lifeexp + usa_gdppercap) / usa_pop +
plot_annotation(title = "Some plots about the United States",
tag_levels = "A")

You can see how this would be really useful for publications!
Animating
Since we have time-scale data here, we could also build an animation that would help us look at our data. What if we wanted to look at how life expectancy (lifeExp) and population (pop) change over time? We could animate over the variable year, and do this by using the function animate(), and set transition_states() to the variable we are giffing over.
Note, I have included closest_state in the subtitle so the viewer can see what is the year at any stage of the animation.
To be able to tell which dot belongs to which country, I added a geom_text_repel() statement, which labels each point but is smart enough to not let the labels overlap.
I have also set pop to be on a log10 scale.
Note I’ve increased the resolution of the gif by putting it in the curly brackets for this code chunk.
# install.packages("transformr")
# if you are having problems with gganimate you may need to install transformr
p <- ggplot(gapminder_americas, aes(x = lifeExp, y = pop, fill = country, label = country)) +
geom_point(shape = 21, color = "black") +
geom_text_repel() +
scale_y_log10() +
theme_classic() +
theme(legend.position = 'none') +
labs(title = "Population and Life Expectancy in the Americas",
subtitle = 'Year: {closest_state}',
x = "Life Expectancy",
y = "Log10 Population") +
transition_states(year)
animate(p)

There are many different ways to transition your data in gganimate - and you can learn more about them here.
Saving my gif
Now I want to save my gif. We can do that simply with the function anim_save() which works a lot like ggsave().
anim_save(filename = "YOUR FILE PATH HERE",
animation = p)
Breakout room exercises
1. Loading data and get set up
Load the palmerpenguins dataset, look at its structure, and view the beginning of the df.
library(palmerpenguins)
str(penguins)
## tibble [344 × 8] (S3: tbl_df/tbl/data.frame)
## $ species : Factor w/ 3 levels "Adelie","Chinstrap",..: 1 1 1 1 1 1 1 1 1 1 ...
## $ island : Factor w/ 3 levels "Biscoe","Dream",..: 3 3 3 3 3 3 3 3 3 3 ...
## $ bill_length_mm : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
## $ bill_depth_mm : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
## $ flipper_length_mm: int [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
## $ body_mass_g : int [1:344] 3750 3800 3250 NA 3450 3650 3625 4675 3475 4250 ...
## $ sex : Factor w/ 2 levels "female","male": 2 1 1 NA 1 2 1 2 NA NA ...
## $ year : int [1:344] 2007 2007 2007 2007 2007 2007 2007 2007 2007 2007 ...
head(penguins)
## # A tibble: 6 x 8
## species island bill_length_mm bill_depth_mm flipper_length_… body_mass_g sex
## <fct> <fct> <dbl> <dbl> <int> <int> <fct>
## 1 Adelie Torge… 39.1 18.7 181 3750 male
## 2 Adelie Torge… 39.5 17.4 186 3800 fema…
## 3 Adelie Torge… 40.3 18 195 3250 fema…
## 4 Adelie Torge… NA NA NA NA <NA>
## 5 Adelie Torge… 36.7 19.3 193 3450 fema…
## 6 Adelie Torge… 39.3 20.6 190 3650 male
## # … with 1 more variable: year <int>
2. Convert bill data from wide to long
Like we did in Code Club 7, convert the two columns about penguin bill dimensions bill_length_mm and bill_depth_mm to two columns called bill_dimension and value. Drop your NAs also. Save this as a new df called penguins_long.
penguins_long <- penguins %>%
drop_na() %>%
pivot_longer(cols = bill_length_mm:bill_depth_mm,
names_to = "bill_dimension",
values_to = "value_mm",
names_prefix = "bill_")
head(penguins_long)
## # A tibble: 6 x 8
## species island flipper_length_… body_mass_g sex year bill_dimension
## <fct> <fct> <int> <int> <fct> <int> <chr>
## 1 Adelie Torge… 181 3750 male 2007 length_mm
## 2 Adelie Torge… 181 3750 male 2007 depth_mm
## 3 Adelie Torge… 186 3800 fema… 2007 length_mm
## 4 Adelie Torge… 186 3800 fema… 2007 depth_mm
## 5 Adelie Torge… 195 3250 fema… 2007 length_mm
## 6 Adelie Torge… 195 3250 fema… 2007 depth_mm
## # … with 1 more variable: value_mm <dbl>
3. Plot body mass as related to bill length and depth
penguins_long %>%
ggplot(aes(x = body_mass_g, y = value_mm)) +
geom_point() +
facet_wrap(vars(bill_dimension))

4. Pretty up your plot
You can do things like change your axis labels, add title, change themes as you see fit. Color your points by sex.
library(hrbrthemes) # for pretty & easy themes
# formatting facet strip text labels
dim_mm <- c("Culman Bill Depth", "Culman Bill Length")
names(dim_mm) <- c("depth_mm", "length_mm")
# this is just one example
penguins_long %>%
ggplot(aes(x = body_mass_g, y = value_mm, color = sex)) +
geom_point() +
theme_ipsum_rc() +
theme(axis.title.x = element_text(hjust = 0.5),
axis.title.y = element_text(hjust = 0.5),
strip.text = element_text(hjust = 0.5)) +
labs(x = "Body Mass (g)",
y = "mm",
title = "Bill length and depth vs. body mass in penguins",
color = "Sex",
caption = "Data from https://allisonhorst.github.io/palmerpenguins/") +
facet_wrap(vars(bill_dimension),
labeller = labeller(bill_dimension = dim_mm))

5. Add a second dimension of faceting by species
penguins_long %>%
ggplot(aes(x = body_mass_g, y = value_mm, color = sex)) +
geom_point() +
theme_ipsum_rc() +
theme(axis.title.x = element_text(hjust = 0.5),
axis.title.y = element_text(hjust = 0.5),
strip.text = element_text(hjust = 0.5)) +
labs(x = "Body Mass (g)",
y = "mm",
title = "Bill length and depth vs. body mass in penguins",
color = "Sex",
caption = "Data from https://allisonhorst.github.io/palmerpenguins/") +
facet_wrap(bill_dimension~species,
labeller = labeller(bill_dimension = dim_mm))

6. Take your plot from 3 and highlight
Using your plot from Exercise 3, highlight the datapoints coming from Dream Island in purple.
unique(penguins_long$island)
## [1] Torgersen Biscoe Dream
## Levels: Biscoe Dream Torgersen
penguins_long %>%
ggplot(aes(x = body_mass_g, y = value_mm)) +
geom_point(color = "purple") +
gghighlight(island == "Dream") +
facet_wrap(vars(bill_dimension))

6. Animating
Plot flipper_length_mm vs. body_mass_g and animate the plot to show only one species at a time.
flipper_by_BW <- penguins %>%
ggplot(aes(x = body_mass_g, y = flipper_length_mm, fill = species)) +
geom_point(shape = 21, color = "black") +
theme_classic() +
theme(legend.position = 'none') +
labs(title = "Population and Life Expectancy in the Americas",
subtitle = 'Penguin Species: {closest_state}',
x = "Body Mass (g)",
y = "Flipper Length (mm)") +
transition_states(species)
animate(flipper_by_BW)

7. Save your gif
anim_save(filename = "YOUR FILE PATH HERE",
animation = flipper_by_BW)
8. Multi-panel plots
We are making a few plots to assemble a multi-panel plot. Let’s remember what data we’re working for.
head(penguins_long)
## # A tibble: 6 x 8
## species island flipper_length_… body_mass_g sex year bill_dimension
## <fct> <fct> <int> <int> <fct> <int> <chr>
## 1 Adelie Torge… 181 3750 male 2007 length_mm
## 2 Adelie Torge… 181 3750 male 2007 depth_mm
## 3 Adelie Torge… 186 3800 fema… 2007 length_mm
## 4 Adelie Torge… 186 3800 fema… 2007 depth_mm
## 5 Adelie Torge… 195 3250 fema… 2007 length_mm
## 6 Adelie Torge… 195 3250 fema… 2007 depth_mm
## # … with 1 more variable: value_mm <dbl>
Boxplot of body_mass_g by sex.
penguins_mass_by_sex <- penguins_long %>%
ggplot(aes(x = sex, y = body_mass_g)) +
geom_boxplot()
penguins_mass_by_sex

Histogram of number of observations per island.
penguins_by_island <- penguins_long %>%
ggplot(aes(y = island, fill = island)) +
geom_histogram(stat = "count")
## Warning: Ignoring unknown parameters: binwidth, bins, pad
penguins_by_island
Distribution of flipper_length_mm by species.
penguins_flipper_species <- penguins_long %>%
ggplot(aes(x = flipper_length_mm, group = species, fill = species)) +
geom_density(alpha = 0.5) +
scale_fill_viridis_d()
penguins_flipper_species

Assemble multi-plot figure using the plots you just made.
penguins_flipper_species / (penguins_mass_by_sex + penguins_by_island) +
plot_annotation(title = "Looking at penguins...",
tag_levels = "A")

LS0tCnRpdGxlOiAiRmFjZXRpbmcsIGFuaW1hdGluZywgYW5kIG11bHRpLXBhbmVsIGZpZ3VyZXMiCmF1dGhvcjogIllvdSEiCmRhdGU6ICIyLzEyLzIwMjEiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIHRoZW1lOiBmbGF0bHkKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIAotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyBJbnRyb2R1Y3Rpb24KCiMjIEluc3RhbGwgcGFja2FnZXMsIGxvYWQgbGlicmFyaWVzCldlIGFyZSBnb2luZyB0byBiZSB1c2luZyBhIGJ1bmNoIG9mIHBhY2thZ2VzIHRvZGF5LgoKVG8gaW5zdGFsbCBhbGwgdGhvc2UgZXhjZXB0IHRpZHl2ZXJzZSB3aGljaCB5b3UgcHJvYmFibHkgYWxyZWFkeSBoYXZlLgpgYGB7ciwgZXZhbCA9IEZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKGMoImdnaGlnaGxpZ2h0IiwKICAgICAgICAgICAgICAgICAgICJnZ2FuaW1hdGUiLAogICAgICAgICAgICAgICAgICAgInBhdGNod29yayIsCiAgICAgICAgICAgICAgICAgICAiZ2dyZXBlbCIsCiAgICAgICAgICAgICAgICAgICAiZ2FwbWluZGVyIikpCmBgYAoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdnaGlnaGxpZ2h0KSAjIGZvciBicmluZ2luZyBhdHRlbnRpb24gdG8gY2VydGFpbiBwYXJ0cyBvZiB5b3VyIHBsb3QKbGlicmFyeShnZ2FuaW1hdGUpICMgZm9yIGFuaW1hdGluZwpsaWJyYXJ5KHBhdGNod29yaykgIyBmb3IgbWFraW5nIG11bHRpLXBhbmVsIHBsb3RzCmxpYnJhcnkoZ2dyZXBlbCkgIyBmb3IgZ2V0dGluZyBsYWJlbHMgdG8gbm90IGJlIG9uIHRvcCBvZiB5b3VyIHBvaW50cwoKIyBkYXRhIGZvciB0b2RheQpsaWJyYXJ5KGdhcG1pbmRlcikKYGBgCgojIyBJbnZlc3RpZ2F0ZSBkYXRhCgpgYGB7cn0KIyBsb29rIGF0IHN0cnVjdHVyZQpnbGltcHNlKGdhcG1pbmRlcikKaGVhZChnYXBtaW5kZXIpCgojIHdoYXQgY29udGluZW50cyBkbyB3ZSBoYXZlPwp1bmlxdWUoZ2FwbWluZGVyJGNvbnRpbmVudCkKYGBgCgoqTm90ZSwgb3VyIGRhdGEgaXMgYWxyZWFkeSBpbiB0aWR5LXN0eWxlIGZvcm1hdC4qICAKCldlIHdpbGwgbG9vayBoZXJlIGp1c3QgYXQgZGF0YSBmcm9tIHRoZSBBbWVyaWNhcyAoTm9ydGggYW5kIFNvdXRoIEFtZXJpY2EpCmBgYHtyfQojIG1ha2UgYSBkZiB3aXRoIGRhdGEgb25seSBmcm9tIHRoZSBBbWVyaWNhcwpnYXBtaW5kZXJfYW1lcmljYXMgPC0gZ2FwbWluZGVyICU+JQogIGZpbHRlcihjb250aW5lbnQgPT0gIkFtZXJpY2FzIikKCiMgd2hhdCBjb3VudHJpZXMgZG8gd2UgaGF2ZT8KdW5pcXVlKGdhcG1pbmRlcl9hbWVyaWNhcyRjb3VudHJ5KQpgYGAKCiMjIFBsb3QgbGlmZSBleHBlY3RhbmN5IG92ZXIgdGltZSwgZm9yIGVhY2ggY291bnRyeQpgYGB7cn0KZ2FwbWluZGVyX2FtZXJpY2FzICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBsaWZlRXhwLCBncm91cCA9IGNvdW50cnksIGNvbG9yID0gY291bnRyeSkpICsKICBnZW9tX2xpbmUoKSAKYGBgCgpUb28gY3Jvd2RlZCB0byBpbnRlcnByZXQgZWFzaWx5LgoKV2hhdCBpZiB3ZSB3YW50IHRvIGhpZ2hsaWdodCBvbmUgcGFydGljdWxhciBjb3VudHJ5IG9mIGludGVyZXN0PyAgTGV0J3MgdHJ5IHRoZSBVbml0ZWQgU3RhdGVzLiAgCgpXaGlsZSB3ZSBhcmUgYXQgaXQsIEkgd2lsbCBhZGQgeCBhbmQgeSBheGlzIGxhYmVscywgYSB0aXRsZSwgc3VidGl0bGUsIGFuZCBjYXB0aW9uIHdpdGggW2BsYWJzKClgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbGFicy5odG1sKS4gIApgYGB7cn0KZ2FwbWluZGVyX2FtZXJpY2FzICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBsaWZlRXhwLCBncm91cCA9IGNvdW50cnksIGNvbG9yID0gY291bnRyeSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2doaWdobGlnaHQoY291bnRyeSA9PSAiVW5pdGVkIFN0YXRlcyIpICsKICBsYWJzKHggPSAiWWVhciIsCiAgICAgICB5ID0gIkxpZmUgRXhwZWN0YW5jeSAoeWVhcnMpIiwKICAgICAgIHRpdGxlID0gIkxpZmUgRXhwZWN0YW5jeSBpbiBDb3VudHJpZXMgaW4gdGhlIEFtZXJpY2FzIiwKICAgICAgIHN1YnRpdGxlID0gIkZyb20gMTk1MiB0byAyMDA3IiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBmcm9tIGdhcG1pbmRlci5vcmciKQpgYGAKCiMjIEZhY2V0aW5nCldoYXQgaWYgd2Ugd2FudCB0byBzZWUgYWxsIHRoZSBkYXRhIGF0IG9uY2UsIGJ1dCBqdXN0IGJlIGFibGUgdG8gYmV0dGVyIGF0dHJpYnV0ZSBlYWNoIGxpbmUgdG8gdGhlIGNvcnJlY3QgY291bnRyeT8gIFdlIGNhbiB1c2UgdGhlIFtwcmluY2lwbGUgb2Ygc21hbGwgbXVsdGlwbGVzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9TbWFsbF9tdWx0aXBsZSM6fjp0ZXh0PUElMjBzbWFsbCUyMG11bHRpcGxlJTIwKHNvbWV0aW1lcyUyMGNhbGxlZCx3YXMlMjBwb3B1bGFyaXplZCUyMGJ5JTIwRWR3YXJkJTIwVHVmdGUuKSwgcG9wdWxhcml6ZWQgYnkgRWR3YXJkIFR1ZnRlLCB0byBtYWtlIGEgc2VyaWVzIG9mIGNoYXJ0cyBhbGwgb24gdGhlIHNhbWUgc2NhbGUgdG8gYWxsb3cgY29tcGFyaXNvbiBiZXR3ZWVuIHRoZW0gZWFzaWx5LiAKCldlIGNhbiBmYWNldCB1c2luZyBbYGZhY2V0X3dyYXBgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZmFjZXRfd3JhcC5odG1sKSB0byBjcmVhdGUgc21hbGwgcGxvdHMgZm9yIGVhY2ggY291bnRyeS4gIElmIHlvdSB3YW50IGEgY2VydGFpbiBudW1iZXIgb2Ygcm93cyBvciBjb2x1bW5zIHlvdSBjYW4gaW5kaWNhdGUgdGhlbSBieSBpbmNsdWRpbmcgYG5jb2xgIGFuZCBgbnJvd2AgaW4gdGhlIGBmYWNldF93cmFwKClgIHN0YXRlbWVudC4KCmBgYHtyLCBmaWcud2lkdGggPSAxNCwgZmlnLmhlaWdodCA9IDh9CmdhcG1pbmRlcl9hbWVyaWNhcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCwgY29sb3IgPSBjb3VudHJ5KSkgKwogIGdlb21fbGluZSgpICsKICBmYWNldF93cmFwKHZhcnMoY291bnRyeSkpICsgIyBmYWNldF93cmFwKH5jb3VudHJ5KSBhbHNvIHdvcmtzCiAgbGFicyh4ID0gIlllYXIiLAogICAgICAgeSA9ICJMaWZlIEV4cGVjdGFuY3kgKHllYXJzKSIsCiAgICAgICB0aXRsZSA9ICJMaWZlIEV4cGVjdGFuY3kgaW4gQ291bnRyaWVzIGluIHRoZSBBbWVyaWNhcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJGcm9tIDE5NTIgdG8gMjAwNyIsCiAgICAgICBjYXB0aW9uID0gIkRhdGEgZnJvbSBnYXBtaW5kZXIub3JnIikKYGBgCgpOb3cgb3VyIGxlZ2VuZCBpcyBub3QgbmVjZXNzYXJ5LCBzbyBsZXQncyByZW1vdmUgaXQuICBMZXQncyBhbHNvIHJlbW92ZSB0aGUgZ3JheSBiYWNrZ3JvdW5kIHNpbmNlIGl0cyBub3QgcmVhbGx5IGRvaW5nIG11Y2ggZm9yIHVzLiAgV2Ugd2lsbCBhbHNvIGNoYW5nZSB0byBgdGhlbWVfbWluaW1hbCgpYCB0byBnZXQgcmlkIG9mIHRoZSBncmV5IGJhY2tncm91bmQgd2hpY2ggSSBkb24ndCB0aGluayB3ZSBuZWVkLgpgYGB7ciwgZmlnLndpZHRoID0gMTQsIGZpZy5oZWlnaHQgPSA4fQpnYXBtaW5kZXJfYW1lcmljYXMgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHApKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IGNvdW50cnkpKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBmYWNldF93cmFwKH5jb3VudHJ5KSArCiAgbGFicyh4ID0gIlllYXIiLAogICAgICAgeSA9ICJMaWZlIEV4cGVjdGFuY3kgKHllYXJzKSIsCiAgICAgICB0aXRsZSA9ICJMaWZlIEV4cGVjdGFuY3kgaW4gQ291bnRyaWVzIGluIHRoZSBBbWVyaWNhcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJGcm9tIDE5NTIgdG8gMjAwNyIsCiAgICAgICBjYXB0aW9uID0gIkRhdGEgZnJvbSBnYXBtaW5kZXIub3JnIikKYGBgCgpXb3cgYmV0dGVyISAgQnV0IG5vdyBpdHMgYSBiaXQgaGFyZCB0byBjb250ZXh0dWFsaXplIHRoZSBsaW5lIGZvciBlYWNoIGNvdW50cnkgdG8gdGhlIHdob2xlIGRhdGFzZXQuICAKCiMjIGdnaGlnaGxpZ2h0CkxldCdzIGJyaW5nIHRoZSByZXN0IG9mIGRhdGEgYmFjayBpbiwgYW5kIGhpZ2hsaWdodCBpbiBlYWNoIGZhY2V0IHRoZSBjb3VudHJ5IG9mIGludGVyZXN0LiAgV2UgY2FuIGRvIHRoaXMgYnkganVzdCBhZGRpbmcgYGdnaGlnaGxpZ2h0KClgIHRvIG91ciBgZ2dwbG90MmAgY2FsbC4KCk5vdGU6IGlmIHlvdSB3YW50IHRvIGFzc2lnbiBzb21ldGhpbmcgaW4gUiB0byBhbiBvYmplY3QsIGFuZCB0aGVuIHZpZXcgaXQsIHlvdSBjYW4gcHV0IHRoZSB3aG9sZSB0aGluZyBpbiBwYXJlbnRoZXNlcywgd2l0aG91dCBoYXZpbmcgdG8gY2FsbCB0aGF0IG9iamVjdCBiYWNrIGF0IHRoZSBlbmQuCmBgYHtyLCBmaWcud2lkdGggPSAxNCwgZmlnLmhlaWdodCA9IDh9CihhbWVyaWNhc19saWZlZXhwIDwtIGdhcG1pbmRlcl9hbWVyaWNhcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbGlmZUV4cCkpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gY291bnRyeSkpICsKICBnZ2hpZ2hsaWdodCgpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGZhY2V0X3dyYXAofmNvdW50cnkpICsKICBsYWJzKHggPSAiWWVhciIsCiAgICAgICB5ID0gIkxpZmUgRXhwZWN0YW5jeSAoeWVhcnMpIiwKICAgICAgIHRpdGxlID0gIkxpZmUgRXhwZWN0YW5jeSBpbiBDb3VudHJpZXMgaW4gdGhlIEFtZXJpY2FzIiwKICAgICAgIHN1YnRpdGxlID0gIkZyb20gMTk1MiB0byAyMDA3IiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBmcm9tIGdhcG1pbmRlci5vcmciKSkKYGBgCgojIyMgQWRqdXN0aW5nIHNjYWxlcwpUaGUgZGVmYXVsdCBpbiBmYWNldGluZyBpcyB0aGF0IHRoZSB4IGFuZCB5LWF4ZXMgZm9yIGVhY2ggcGxvdCBhcmUgYWxsIHRoZSBzYW1lLiAgVGhpcyBhaWRzIGluIHRoZSBpbnRlcnByZXRhdGlvbiBvZiBlYWNoIHNtYWxsIHBsb3QgaW4gcmVsYXRpb24gdG8gdGhlIG90aGVycywgYnV0IHNvbWV0aW1lcyB5b3UgbWF5IHdhbnQgZnJlZWRvbSB0byBhZGp1c3QgeW91ciBheGVzLgoKRm9yIGV4YW1wbGUsIGlmIHdlIHdhbnRlZCB0byBwbG90IHBvcHVsYXRpb24gb3ZlciB0aW1lLCBpZiB3ZSB1c2VkIHRoZSBzYW1lIHNjYWxlLCBpdCB3b3VsZCBiZSByZWFsbHkgaGFyZCB0byBzZWUgdHJlbmRzIHdpdGhpbiBhIGNvdW50cnkuCgpgYGB7cn0KKGFtZXJpY2FzX3BvcCA8LSBnYXBtaW5kZXJfYW1lcmljYXMgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IHBvcCkpICsKICBnZW9tX2xpbmUoYWVzKGNvbG9yID0gY291bnRyeSkpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGZhY2V0X3dyYXAofmNvdW50cnkpICsKICBsYWJzKHggPSAiWWVhciIsCiAgICAgICB5ID0gIlBvcHVsYXRpb24iLAogICAgICAgdGl0bGUgPSAiUG9wdWxhdGlvbiBpbiBDb3VudHJpZXMgaW4gdGhlIEFtZXJpY2FzIiwKICAgICAgIHN1YnRpdGxlID0gIkZyb20gMTk1MiB0byAyMDA3IiwKICAgICAgIGNhcHRpb24gPSAiRGF0YSBmcm9tIGdhcG1pbmRlci5vcmciKSkKYGBgCgpMZXQncyBjaGFuZ2UgdGhlIHNjYWxlcyBzbyB0aGF0IHRoZSB5LWF4aXMgaXMgImZyZWUiIC0gaS5lLiwgZWFjaCBwbG90IHdpbGwgaGF2ZSBhbiBpbmRlcGVuZGVudCB5LWF4aXMuICBOb3RlLCB3aGVuIHlvdSBkbyB0aGlzLCB5b3UgYXJlbid0IHJlYWxseSB1c2luZyB0aGUgcHJpbmNpcGxlIG9mIHNtYWxsIG11bHRpcGxlcyBhbnltb3JlLCBzaW5jZSB0aGUgZGF0YSBpc24ndCBhbGwgb24gY29tcGFyYWJsZSBzY2FsZXMuCmBgYHtyLCBmaWcud2lkdGggPSAxNCwgZmlnLmhlaWdodCA9IDh9CmdhcG1pbmRlcl9hbWVyaWNhcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gcG9wKSkgKwogIGdlb21fbGluZShhZXMoY29sb3IgPSBjb3VudHJ5KSkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgZmFjZXRfd3JhcCh+Y291bnRyeSwKICAgICAgICAgICAgIHNjYWxlcyA9ICJmcmVlX3kiKSArCiAgbGFicyh4ID0gIlllYXIiLAogICAgICAgeSA9ICJQb3B1bGF0aW9uIiwKICAgICAgIHRpdGxlID0gIlBvcHVsYXRpb24gb2YgQ291bnRyaWVzIGluIHRoZSBBbWVyaWNhcyIsCiAgICAgICBzdWJ0aXRsZSA9ICJGcm9tIDE5NTIgdG8gMjAwNyIsCiAgICAgICBjYXB0aW9uID0gIkRhdGEgZnJvbSBnYXBtaW5kZXIub3JnIikKYGBgCgpUaGUgZGVmYXVsdCBmb3IgYHNjYWxlc2AgaXMgYCJmaXhlZCJgLCBidXQgeW91IGNhbiBhbHNvIHNldCB0byBiZSBgImZyZWVfeCJgLCBgImZyZWVfeSJgLCBvciBgImZyZWUiYCwgd2hpY2ggbWVhbnMgYm90aCB4IGFuZCB5IGFyZSBmcmVlLgoKIyMgTXVsdGktcGFuZWwgcGxvdHMKV2hhdCBpZiBJIHRha2UgcGxvdHMgSSd2ZSBhbHJlYWR5IG1hZGUgYW5kIGFzc2VtYmxlIHRoZW0gdG9nZXRoZXI/ICBZb3UgY2FuIGRvIHRoYXQgc2ltcGx5IHdpdGggdGhlIHBhY2thZ2UgW2BwYXRjaHdvcmsoKWBdKGh0dHBzOi8vcGF0Y2h3b3JrLmRhdGEtaW1hZ2luaXN0LmNvbS8pLgoKWW91IGNhbiB1c2UgdGhlIHN5bnRheDoKKiBgcGxvdDEgKyBwbG90MmAgdG8gZ2V0IHR3byBwbG90cyBuZXh0IHRvIGVhY2ggb3RoZXIKKiBgcGxvdDEgLyBwbG90MmAgdG8gZ2V0IHR3byBwbG90cyBzdGFja2VkIHZlcnRpY2FsbHkKKiBgcGxvdDEgfCAocGxvdDIgKyBwbG90MylgIHRvIGdldCBwbG90MSBpbiB0aGUgZmlyc3Qgcm93LCBhbmQgcGxvdHMgMiBhbmQgMyBpbiBhIHNlY29uZCByb3cKCllvdSBjYW4gdXNlIFtgcGxvdF9hbm5vdGF0aW9uKClgXShodHRwczovL3BhdGNod29yay5kYXRhLWltYWdpbmlzdC5jb20vcmVmZXJlbmNlL3Bsb3RfYW5ub3RhdGlvbi5odG1sKSB0byBpbmRpY2F0ZSB5b3VyIHBsb3RzIHdpdGggbGV0dGVycyBvciBudW1iZXJzLgoKSSBhbSBnb2luZyB0byBtYWtlIHNvbWUgcXVpY2sgcGxvdHMgc28gd2UgY2FuIHNlZSBob3cgaXQgd29ya3MuICBMZXQncyBsb29rIGF0IHNvbWUgcGxvdHMgb2YgdGhlIFVuaXRlZCBTdGF0ZXMuCgpgYGB7cn0KIyBtYWtlIGRmIHdpdGgganVzdCBVbml0ZWQgU3RhdGVzIGRhdGEKZ2FwbWluZGVyX3VzYSA8LSBnYXBtaW5kZXIgJT4lCiAgZmlsdGVyKGNvdW50cnkgPT0gIlVuaXRlZCBTdGF0ZXMiKQoKIyBtYWtlIHNvbWUgcGxvdHMKKHVzYV9saWZlZXhwIDwtIGdhcG1pbmRlcl91c2EgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGxpZmVFeHApKSArCiAgZ2VvbV9wb2ludCgpKQoKKHVzYV9nZHBwZXJjYXAgPC0gZ2FwbWluZGVyX3VzYSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gZ2RwUGVyY2FwKSkgKwogIGdlb21fbGluZSgpKQoKKHVzYV9wb3AgPC0gZ2FwbWluZGVyX3VzYSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gcG9wKSkgKwogIGdlb21fY29sKCkpCmBgYAoKTWFrZSBtdWx0aS1wYW5lbCBwbG90cy4gIElmIHlvdSBuZWVkIHRvIHdyYXAgYXJvdW5kIGEgbGluZSwgbWFrZSBzdXJlIHlvdSBkb24ndCBzdGFydCB5b3VyIGxpbmUgd2l0aCB0aGUgKywgaXQgd29uJ3Qgd29yay4KYGBge3J9Cih1c2FfbGlmZWV4cCArIHVzYV9nZHBwZXJjYXApIC8gdXNhX3BvcCArCnBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJTb21lIHBsb3RzIGFib3V0IHRoZSBVbml0ZWQgU3RhdGVzIiwKICAgICAgICAgICAgICAgICAgdGFnX2xldmVscyA9ICJBIikKYGBgCgpZb3UgY2FuIHNlZSBob3cgdGhpcyB3b3VsZCBiZSByZWFsbHkgdXNlZnVsIGZvciBwdWJsaWNhdGlvbnMhCgojIyBBbmltYXRpbmcKU2luY2Ugd2UgaGF2ZSB0aW1lLXNjYWxlIGRhdGEgaGVyZSwgd2UgY291bGQgYWxzbyBidWlsZCBhbiBhbmltYXRpb24gdGhhdCB3b3VsZCBoZWxwIHVzIGxvb2sgYXQgb3VyIGRhdGEuICBXaGF0IGlmIHdlIHdhbnRlZCB0byBsb29rIGF0IGhvdyBsaWZlIGV4cGVjdGFuY3kgKGBsaWZlRXhwYCkgYW5kIHBvcHVsYXRpb24gKGBwb3BgKSBjaGFuZ2Ugb3ZlciB0aW1lPyAgV2UgY291bGQgYW5pbWF0ZSBvdmVyIHRoZSB2YXJpYWJsZSBgeWVhcmAsIGFuZCBkbyB0aGlzIGJ5IHVzaW5nIHRoZSBmdW5jdGlvbiBbYGFuaW1hdGUoKWBdKGh0dHBzOi8vZ2dhbmltYXRlLmNvbS9yZWZlcmVuY2UvYW5pbWF0ZS5odG1sKSwgYW5kIHNldCBbYHRyYW5zaXRpb25fc3RhdGVzKClgXShodHRwczovL2dnYW5pbWF0ZS5jb20vcmVmZXJlbmNlL3RyYW5zaXRpb25fc3RhdGVzLmh0bWwpIHRvIHRoZSB2YXJpYWJsZSB3ZSBhcmUgZ2lmZmluZyBvdmVyLiAgCgpOb3RlLCBJIGhhdmUgaW5jbHVkZWQgYGNsb3Nlc3Rfc3RhdGVgIGluIHRoZSBzdWJ0aXRsZSBzbyB0aGUgdmlld2VyIGNhbiBzZWUgd2hhdCBpcyB0aGUgeWVhciBhdCBhbnkgc3RhZ2Ugb2YgdGhlIGFuaW1hdGlvbi4KClRvIGJlIGFibGUgdG8gdGVsbCB3aGljaCBkb3QgYmVsb25ncyB0byB3aGljaCBjb3VudHJ5LCBJIGFkZGVkIGEgW2BnZW9tX3RleHRfcmVwZWwoKWBdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9nZ3JlcGVsL3ZlcnNpb25zLzAuOS4xL3RvcGljcy9nZW9tX2xhYmVsX3JlcGVsKSBzdGF0ZW1lbnQsIHdoaWNoIGxhYmVscyBlYWNoIHBvaW50IGJ1dCBpcyBzbWFydCBlbm91Z2ggdG8gbm90IGxldCB0aGUgbGFiZWxzIG92ZXJsYXAuCgpJIGhhdmUgYWxzbyBzZXQgYHBvcGAgdG8gYmUgb24gYSBsb2cxMCBzY2FsZS4KCk5vdGUgSSd2ZSBpbmNyZWFzZWQgdGhlIHJlc29sdXRpb24gb2YgdGhlIGdpZiBieSBwdXR0aW5nIGl0IGluIHRoZSBjdXJseSBicmFja2V0cyBmb3IgdGhpcyBjb2RlIGNodW5rLgoKYGBge3IsIGNhY2hlID0gVFJVRSwgZHBpID0gNjAwfQojIGluc3RhbGwucGFja2FnZXMoInRyYW5zZm9ybXIiKSAKIyBpZiB5b3UgYXJlIGhhdmluZyBwcm9ibGVtcyB3aXRoIGdnYW5pbWF0ZSB5b3UgbWF5IG5lZWQgdG8gaW5zdGFsbCB0cmFuc2Zvcm1yCgpwIDwtIGdncGxvdChnYXBtaW5kZXJfYW1lcmljYXMsIGFlcyh4ID0gbGlmZUV4cCwgeSA9IHBvcCwgZmlsbCA9IGNvdW50cnksIGxhYmVsID0gY291bnRyeSkpICsKICBnZW9tX3BvaW50KHNoYXBlID0gMjEsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dF9yZXBlbCgpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKSArCiAgbGFicyh0aXRsZSA9ICJQb3B1bGF0aW9uIGFuZCBMaWZlIEV4cGVjdGFuY3kgaW4gdGhlIEFtZXJpY2FzIiwKICAgICAgIHN1YnRpdGxlID0gJ1llYXI6IHtjbG9zZXN0X3N0YXRlfScsIAogICAgICAgeCA9ICJMaWZlIEV4cGVjdGFuY3kiLCAKICAgICAgIHkgPSAiTG9nMTAgUG9wdWxhdGlvbiIpICsKICB0cmFuc2l0aW9uX3N0YXRlcyh5ZWFyKSAKCmFuaW1hdGUocCkKYGBgCgpUaGVyZSBhcmUgbWFueSBkaWZmZXJlbnQgd2F5cyB0byB0cmFuc2l0aW9uIHlvdXIgZGF0YSBpbiBgZ2dhbmltYXRlYCAtIGFuZCB5b3UgY2FuIGxlYXJuIG1vcmUgYWJvdXQgdGhlbSBbaGVyZV0oaHR0cHM6Ly9nZ2FuaW1hdGUuY29tL3JlZmVyZW5jZS9pbmRleC5odG1sKS4KCiMjIyBTYXZpbmcgbXkgZ2lmCk5vdyBJIHdhbnQgdG8gc2F2ZSBteSBnaWYuICBXZSBjYW4gZG8gdGhhdCBzaW1wbHkgd2l0aCB0aGUgZnVuY3Rpb24gW2BhbmltX3NhdmUoKWBdKGh0dHBzOi8vZ2dhbmltYXRlLmNvbS9yZWZlcmVuY2UvYW5pbV9zYXZlLmh0bWwpIHdoaWNoIHdvcmtzIGEgbG90IGxpa2UgYGdnc2F2ZSgpYC4gIAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KYW5pbV9zYXZlKGZpbGVuYW1lID0gIllPVVIgRklMRSBQQVRIIEhFUkUiLAogICAgICAgICAgYW5pbWF0aW9uID0gcCkKYGBgCgojIEJyZWFrb3V0IHJvb20gZXhlcmNpc2VzCgojIyAxLiBMb2FkaW5nIGRhdGEgYW5kIGdldCBzZXQgdXAKTG9hZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBkYXRhc2V0LCBsb29rIGF0IGl0cyBzdHJ1Y3R1cmUsIGFuZCB2aWV3IHRoZSBiZWdpbm5pbmcgb2YgdGhlIGRmLgpgYGB7cn0KbGlicmFyeShwYWxtZXJwZW5ndWlucykKc3RyKHBlbmd1aW5zKQpoZWFkKHBlbmd1aW5zKQpgYGAKCiMjIDIuIENvbnZlcnQgYmlsbCBkYXRhIGZyb20gd2lkZSB0byBsb25nCkxpa2Ugd2UgZGlkIGluIFtDb2RlIENsdWIgN10oaHR0cHM6Ly9iaW9kYXNoLmdpdGh1Yi5pby9jb2RlY2x1Yi8wOF9waXZvdGluZy8pLCBjb252ZXJ0IHRoZSB0d28gY29sdW1ucyBhYm91dCBwZW5ndWluIGJpbGwgZGltZW5zaW9ucyBgYmlsbF9sZW5ndGhfbW1gIGFuZCBgYmlsbF9kZXB0aF9tbWAgdG8gdHdvIGNvbHVtbnMgY2FsbGVkIGBiaWxsX2RpbWVuc2lvbmAgYW5kIGB2YWx1ZWAuICBEcm9wIHlvdXIgTkFzIGFsc28uICBTYXZlIHRoaXMgYXMgYSBuZXcgZGYgY2FsbGVkIGBwZW5ndWluc19sb25nYC4KYGBge3J9CnBlbmd1aW5zX2xvbmcgPC0gcGVuZ3VpbnMgJT4lCiAgZHJvcF9uYSgpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gYmlsbF9sZW5ndGhfbW06YmlsbF9kZXB0aF9tbSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiYmlsbF9kaW1lbnNpb24iLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAidmFsdWVfbW0iLAogICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAiYmlsbF8iKQoKaGVhZChwZW5ndWluc19sb25nKQpgYGAKCiMjIDMuIFBsb3QgYm9keSBtYXNzIGFzIHJlbGF0ZWQgdG8gYmlsbCBsZW5ndGggYW5kIGRlcHRoCmBgYHtyfQpwZW5ndWluc19sb25nICU+JQogIGdncGxvdChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gdmFsdWVfbW0pKSArCiAgZ2VvbV9wb2ludCgpICsKICBmYWNldF93cmFwKHZhcnMoYmlsbF9kaW1lbnNpb24pKQpgYGAKCiMjIDQuIFByZXR0eSB1cCB5b3VyIHBsb3QKWW91IGNhbiBkbyB0aGluZ3MgbGlrZSBjaGFuZ2UgeW91ciBheGlzIGxhYmVscywgYWRkIHRpdGxlLCBjaGFuZ2UgdGhlbWVzIGFzIHlvdSBzZWUgZml0LiAgQ29sb3IgeW91ciBwb2ludHMgYnkgc2V4LgpgYGB7cn0KbGlicmFyeShocmJydGhlbWVzKSAjIGZvciBwcmV0dHkgJiBlYXN5IHRoZW1lcwoKIyBmb3JtYXR0aW5nIGZhY2V0IHN0cmlwIHRleHQgbGFiZWxzCmRpbV9tbSA8LSBjKCJDdWxtYW4gQmlsbCBEZXB0aCIsICJDdWxtYW4gQmlsbCBMZW5ndGgiKQpuYW1lcyhkaW1fbW0pIDwtIGMoImRlcHRoX21tIiwgImxlbmd0aF9tbSIpCgojIHRoaXMgaXMganVzdCBvbmUgZXhhbXBsZQpwZW5ndWluc19sb25nICU+JQogIGdncGxvdChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gdmFsdWVfbW0sIGNvbG9yID0gc2V4KSkgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWVfaXBzdW1fcmMoKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgbGFicyh4ID0gIkJvZHkgTWFzcyAoZykiLAogICAgICAgeSA9ICJtbSIsCiAgICAgICB0aXRsZSA9ICJCaWxsIGxlbmd0aCBhbmQgZGVwdGggdnMuIGJvZHkgbWFzcyBpbiBwZW5ndWlucyIsCiAgICAgICBjb2xvciA9ICJTZXgiLAogICAgICAgY2FwdGlvbiA9ICJEYXRhIGZyb20gaHR0cHM6Ly9hbGxpc29uaG9yc3QuZ2l0aHViLmlvL3BhbG1lcnBlbmd1aW5zLyIpICsKICBmYWNldF93cmFwKHZhcnMoYmlsbF9kaW1lbnNpb24pLAogICAgICAgICAgICAgbGFiZWxsZXIgPSBsYWJlbGxlcihiaWxsX2RpbWVuc2lvbiA9IGRpbV9tbSkpCmBgYAoKIyMgNS4gQWRkIGEgc2Vjb25kIGRpbWVuc2lvbiBvZiBmYWNldGluZyBieSBzcGVjaWVzCmBgYHtyfQpwZW5ndWluc19sb25nICU+JQogIGdncGxvdChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gdmFsdWVfbW0sIGNvbG9yID0gc2V4KSkgKwogIGdlb21fcG9pbnQoKSArCiAgdGhlbWVfaXBzdW1fcmMoKSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSwKICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpLAogICAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArCiAgbGFicyh4ID0gIkJvZHkgTWFzcyAoZykiLAogICAgICAgeSA9ICJtbSIsCiAgICAgICB0aXRsZSA9ICJCaWxsIGxlbmd0aCBhbmQgZGVwdGggdnMuIGJvZHkgbWFzcyBpbiBwZW5ndWlucyIsCiAgICAgICBjb2xvciA9ICJTZXgiLAogICAgICAgY2FwdGlvbiA9ICJEYXRhIGZyb20gaHR0cHM6Ly9hbGxpc29uaG9yc3QuZ2l0aHViLmlvL3BhbG1lcnBlbmd1aW5zLyIpICsKICBmYWNldF93cmFwKGJpbGxfZGltZW5zaW9ufnNwZWNpZXMsCiAgICAgICAgICAgICBsYWJlbGxlciA9IGxhYmVsbGVyKGJpbGxfZGltZW5zaW9uID0gZGltX21tKSkKYGBgCgojIyA2LiBUYWtlIHlvdXIgcGxvdCBmcm9tIDMgYW5kIGhpZ2hsaWdodApVc2luZyB5b3VyIHBsb3QgZnJvbSBFeGVyY2lzZSAzLCBoaWdobGlnaHQgdGhlIGRhdGFwb2ludHMgY29taW5nIGZyb20gRHJlYW0gSXNsYW5kIGluIHB1cnBsZS4KYGBge3J9CnVuaXF1ZShwZW5ndWluc19sb25nJGlzbGFuZCkKCnBlbmd1aW5zX2xvbmcgJT4lCiAgZ2dwbG90KGFlcyh4ID0gYm9keV9tYXNzX2csIHkgPSB2YWx1ZV9tbSkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gInB1cnBsZSIpICsKICBnZ2hpZ2hsaWdodChpc2xhbmQgPT0gIkRyZWFtIikgKwogIGZhY2V0X3dyYXAodmFycyhiaWxsX2RpbWVuc2lvbikpCmBgYAoKIyMgNi4gQW5pbWF0aW5nClBsb3QgYGZsaXBwZXJfbGVuZ3RoX21tYCB2cy4gYGJvZHlfbWFzc19nYCBhbmQgYW5pbWF0ZSB0aGUgcGxvdCB0byBzaG93IG9ubHkgb25lIGBzcGVjaWVzYCBhdCBhIHRpbWUuCmBgYHtyLCBjYWNoZSA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQpmbGlwcGVyX2J5X0JXIDwtIHBlbmd1aW5zICU+JQogIGdncGxvdChhZXMoeCA9IGJvZHlfbWFzc19nLCB5ID0gZmxpcHBlcl9sZW5ndGhfbW0sIGZpbGwgPSBzcGVjaWVzKSkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAyMSwgY29sb3IgPSAiYmxhY2siKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAnbm9uZScpICsKICBsYWJzKHRpdGxlID0gIlBvcHVsYXRpb24gYW5kIExpZmUgRXhwZWN0YW5jeSBpbiB0aGUgQW1lcmljYXMiLAogICAgICAgc3VidGl0bGUgPSAnUGVuZ3VpbiBTcGVjaWVzOiB7Y2xvc2VzdF9zdGF0ZX0nLCAKICAgICAgIHggPSAiQm9keSBNYXNzIChnKSIsIAogICAgICAgeSA9ICJGbGlwcGVyIExlbmd0aCAobW0pIikgKwogIHRyYW5zaXRpb25fc3RhdGVzKHNwZWNpZXMpIAoKYW5pbWF0ZShmbGlwcGVyX2J5X0JXKQpgYGAKCiMjIDcuIFNhdmUgeW91ciBnaWYKYGBge3IsIGV2YWwgPSBGQUxTRX0KYW5pbV9zYXZlKGZpbGVuYW1lID0gIllPVVIgRklMRSBQQVRIIEhFUkUiLAogICAgICAgICAgYW5pbWF0aW9uID0gZmxpcHBlcl9ieV9CVykKYGBgCgojIyA4LiBNdWx0aS1wYW5lbCBwbG90cwpXZSBhcmUgbWFraW5nIGEgZmV3IHBsb3RzIHRvIGFzc2VtYmxlIGEgbXVsdGktcGFuZWwgcGxvdC4gIExldCdzIHJlbWVtYmVyIHdoYXQgZGF0YSB3ZSdyZSB3b3JraW5nIGZvci4KYGBge3J9CmhlYWQocGVuZ3VpbnNfbG9uZykKYGBgCkJveHBsb3Qgb2YgYGJvZHlfbWFzc19nYCBieSBgc2V4YC4KYGBge3J9CnBlbmd1aW5zX21hc3NfYnlfc2V4IDwtIHBlbmd1aW5zX2xvbmcgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc2V4LCB5ID0gYm9keV9tYXNzX2cpKSArCiAgZ2VvbV9ib3hwbG90KCkKCnBlbmd1aW5zX21hc3NfYnlfc2V4CmBgYAoKSGlzdG9ncmFtIG9mIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgcGVyIGBpc2xhbmRgLgpgYGB7cn0KcGVuZ3VpbnNfYnlfaXNsYW5kIDwtIHBlbmd1aW5zX2xvbmcgJT4lCiAgZ2dwbG90KGFlcyh5ID0gaXNsYW5kLCBmaWxsID0gaXNsYW5kKSkgKwogIGdlb21faGlzdG9ncmFtKHN0YXQgPSAiY291bnQiKQoKcGVuZ3VpbnNfYnlfaXNsYW5kCmBgYApEaXN0cmlidXRpb24gb2YgYGZsaXBwZXJfbGVuZ3RoX21tYCBieSBgc3BlY2llc2AuCmBgYHtyfQpwZW5ndWluc19mbGlwcGVyX3NwZWNpZXMgPC0gcGVuZ3VpbnNfbG9uZyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBmbGlwcGVyX2xlbmd0aF9tbSwgZ3JvdXAgPSBzcGVjaWVzLCBmaWxsID0gc3BlY2llcykpICsKICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjUpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpCgpwZW5ndWluc19mbGlwcGVyX3NwZWNpZXMKYGBgCgpBc3NlbWJsZSBtdWx0aS1wbG90IGZpZ3VyZSB1c2luZyB0aGUgcGxvdHMgeW91IGp1c3QgbWFkZS4KYGBge3J9CnBlbmd1aW5zX2ZsaXBwZXJfc3BlY2llcyAvIChwZW5ndWluc19tYXNzX2J5X3NleCArIHBlbmd1aW5zX2J5X2lzbGFuZCkgKwogIHBsb3RfYW5ub3RhdGlvbih0aXRsZSA9ICJMb29raW5nIGF0IHBlbmd1aW5zLi4uIiwKICAgICAgICAgICAgICAgICAgdGFnX2xldmVscyA9ICJBIikKYGBgCgo=